package rexsee.cartoon;

import java.util.HashMap;
import java.util.Iterator;

import rexsee.core.browser.clazz.LogListeners;
import rexsee.core.style.PaintSheet;
import rexsee.core.utilities.Utilities;
import rexsee.xml.XmlElement;
import android.graphics.Camera;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Path.FillType;
import android.graphics.Rect;
import android.graphics.RectF;

public class Actions {

	public final Parser parser;

	private final HashMap<String, Action> actions = new HashMap<String, Action>();

	public long bitmapSize = 0;

	public Actions(Parser cartoonParser) {
		this.parser = cartoonParser;
	}

	public void parse(XmlElement element) {
		if (element.childs == null || element.childs.size() == 0) return;
		for (int i = 0; i < element.childs.size(); i++) {
			Action action = new Action(this, parser.canvasWidth, parser.canvasHeight, element.childs.get(i));
			if (action.id != null) actions.put(action.id, action);
		}
	}

	public void destroy() {
		Iterator<Action> iterator = actions.values().iterator();
		while (iterator.hasNext()) {
			Action action = iterator.next();
			if (action != null) action.destroy();
		}
		actions.clear();
		parser.logListener.run(parser.context, parser.browser, LogListeners.LOG_TYPE_MESSAGE, "Cached bitmap size after destroy:" + bitmapSize);
	}

	public void removeAction(String id) {
		if (actions.containsKey(id)) {
			actions.get(id).destroy();
			actions.remove(id);
		}
	}
	public Action getAction(String id) {
		return (id == null) ? null : actions.get(id);
	}
	public int size() {
		return actions.size();
	}

	public static Rect getRect(String str) {
		if (str == null) return null;
		int[] ints = Utilities.getIntArray(str, 4, 0);
		if (ints == null) return null;
		return new Rect(ints[0], ints[1], ints[2], ints[3]);
	}
	public static RectF getRectF(String str) {
		if (str == null) return null;
		float[] floats = Utilities.getFloatArray(str, 4, 0);
		if (floats == null) return null;
		return new RectF(floats[0], floats[1], floats[2], floats[3]);
	}

	public static Matrix getMatrix(String str, int width, int height) {

		if (str == null || str.trim().equals("")) return null;

		Matrix matrix = new Matrix();

		String[] argus = str.split(";");
		for (int i = 0; i < argus.length; i++) {

			Assign a = new Assign(argus[i]);
			if (a.isNull()) continue;

			if (a.key.equalsIgnoreCase("clear") || a.key.equalsIgnoreCase("reset")) {
				matrix.reset();
			} else if (a.key.equalsIgnoreCase("matrix")) {
				float[] array = Utilities.getFloatArray(a.value, 9, 0);
				if (array != null) matrix.setValues(array);
			} else if (a.key.equalsIgnoreCase("rotate")) {
				float x, y, z, centerX, centerY, centerZ;
				float[] array = Utilities.getFloatArray(a.value, 0);
				if (array == null) continue;
				if (array.length == 3) {
					x = array[0];
					y = array[1];
					z = array[2];
					centerX = width / 2;
					centerY = height / 2;
					centerZ = 0;
				} else if (array.length == 6) {
					x = array[0];
					y = array[1];
					z = array[2];
					centerX = (array[3] > 1 || array[3] < -1) ? array[3] : width * array[3];
					centerY = (array[4] > 1 || array[4] < -1) ? array[4] : height * array[4];
					centerZ = array[5];
				} else continue;
				if (x == 0 && y == 0) {
					matrix.postRotate(z, centerX, centerY);
				} else {
					Camera camera = new Camera();
					if (x != 0) camera.rotateX(x);
					if (y != 0) camera.rotateY(y);
					if (z != 0) camera.rotateZ(z);
					camera.translate(0.0f, 0.0f, centerZ);
					camera.getMatrix(matrix);
					matrix.preTranslate(-centerX, -centerY);
					matrix.postTranslate(centerX, centerY);
				}
			} else if (a.key.equalsIgnoreCase("translate")) {
				float x, y, z, centerX, centerY;
				float[] array = Utilities.getFloatArray(a.value, 0);
				if (array == null) continue;
				if (array.length == 3) {
					x = array[0];
					y = array[1];
					z = array[2];
					centerX = width / 2;
					centerY = height / 2;
				} else if (array.length == 5) {
					x = array[0];
					y = array[1];
					z = array[2];
					centerX = (array[3] > 1 || array[3] < -1) ? array[3] : width * array[3];
					centerY = (array[4] > 1 || array[4] < -1) ? array[4] : height * array[4];
				} else continue;
				if (z == 0) {
					matrix.postTranslate(x, y);
				} else {
					Camera camera = new Camera();
					camera.translate(x, y, z);
					camera.getMatrix(matrix);
					matrix.preTranslate(-centerX, -centerY);
					matrix.postTranslate(centerX, centerY);
				}
			} else if (a.key.equalsIgnoreCase("scale")) {
				float scaleX, scaleY, centerX, centerY;
				float[] array = Utilities.getFloatArray(a.value, 0);
				if (array == null) continue;
				if (array.length == 2) {
					scaleX = array[0];
					scaleY = array[1];
					centerX = width / 2;
					centerY = height / 2;
				} else if (array.length == 4) {
					scaleX = array[0];
					scaleY = array[1];
					centerX = (array[2] > 1 || array[2] < -1) ? array[2] : width * array[2];
					centerY = (array[3] > 1 || array[3] < -1) ? array[3] : height * array[3];
				} else continue;
				matrix.postScale(scaleX, scaleY, centerX, centerY);
			} else if (a.key.equalsIgnoreCase("skew")) {
				float skewX, skewY, centerX, centerY;
				float[] array = Utilities.getFloatArray(a.value, 0);
				if (array == null) continue;
				if (array.length == 2) {
					skewX = array[0];
					skewY = array[1];
					centerX = width / 2;
					centerY = height / 2;
				} else if (array.length == 4) {
					skewX = array[0];
					skewY = array[1];
					centerX = (array[2] > 1 || array[2] < -1) ? array[2] : width * array[2];
					centerY = (array[3] > 1 || array[3] < -1) ? array[3] : height * array[3];
				} else continue;
				matrix.postSkew(skewX, skewY, centerX, centerY);
			}

		}

		return matrix;

	}

	public static Path getPath(String str) {

		if (str == null || str.trim().equals("")) return null;

		Path path = new Path();

		String[] argus = str.split(";");

		for (int i = 0; i < argus.length; i++) {

			Assign a = new Assign(argus[i]);
			if (a.isNull()) continue;

			if (a.key.equalsIgnoreCase("reset")) {
				path.reset();
			} else if (a.key.equalsIgnoreCase("close")) {
				path.close();
			} else if (a.key.equalsIgnoreCase("toggleInverseFillType")) {
				path.toggleInverseFillType();
			} else if (a.key.equalsIgnoreCase("setFillType")) {
				if (a.value.equalsIgnoreCase("EVEN_ODD")) {
					path.setFillType(FillType.EVEN_ODD);
				} else if (a.value.equalsIgnoreCase("INVERSE_EVEN_ODD")) {
					path.setFillType(FillType.INVERSE_EVEN_ODD);
				} else if (a.value.equalsIgnoreCase("INVERSE_WINDING")) {
					path.setFillType(FillType.INVERSE_WINDING);
				} else if (a.value.equalsIgnoreCase("WINDING")) {
					path.setFillType(FillType.WINDING);
				}
			} else if (a.key.equalsIgnoreCase("transform")) {
				float[] array = Utilities.getFloatArray(a.value, 9, 0);
				if (array != null) {
					Matrix matrix = new Matrix();
					matrix.setValues(array);
					path.transform(matrix);
				}
			} else if (a.key.equalsIgnoreCase("offset")) {
				float[] array = Utilities.getFloatArray(a.value, 2, 0);
				if (array != null) path.offset(array[0], array[1]);
			} else if (a.key.equalsIgnoreCase("setLastPoint") || a.key.equalsIgnoreCase("LastPoint") || a.key.equalsIgnoreCase("last")) {
				float[] array = Utilities.getFloatArray(a.value, 2, 0);
				if (array != null) path.setLastPoint(array[0], array[1]);

				//shape to
			} else if (a.key.equalsIgnoreCase("moveTo")) {
				float[] array = Utilities.getFloatArray(a.value, 2, 0);
				if (array != null) path.moveTo(array[0], array[1]);
			} else if (a.key.equalsIgnoreCase("lineTo")) {
				float[] array = Utilities.getFloatArray(a.value, 2, 0);
				if (array != null) path.lineTo(array[0], array[1]);
			} else if (a.key.equalsIgnoreCase("quadTo")) {
				float[] array = Utilities.getFloatArray(a.value, 4, 0);
				if (array != null) path.quadTo(array[0], array[1], array[2], array[3]);
			} else if (a.key.equalsIgnoreCase("cubicTo")) {
				float[] array = Utilities.getFloatArray(a.value, 6, 0);
				if (array != null) path.cubicTo(array[0], array[1], array[2], array[3], array[4], array[5]);
			} else if (a.key.equalsIgnoreCase("arcTo")) {
				float[] array = Utilities.getFloatArray(a.value, 0);
				if (array == null) {
					continue;
				} else if (array.length == 6) {
					RectF rectF = new RectF(array[0], array[1], array[2], array[3]);
					if (array != null) path.arcTo(rectF, array[4], array[5], false);
				} else if (array.length == 7) {
					RectF rectF = new RectF(array[0], array[1], array[2], array[3]);
					if (array != null) path.arcTo(rectF, array[4], array[5], array[6] == 1);
				}

				//add shape
			} else if (a.key.equalsIgnoreCase("addArc") || a.key.equalsIgnoreCase("arc")) {
				float[] array = Utilities.getFloatArray(a.value, 6, 0);
				if (array != null) {
					RectF rectF = new RectF(array[0], array[1], array[2], array[3]);
					path.addArc(rectF, array[4], array[5]);
				}
			} else if (a.key.equalsIgnoreCase("addCycle") || a.key.equalsIgnoreCase("cycle")) {
				float[] array = Utilities.getFloatArray(a.value, 0);
				if (array == null) {
					continue;
				} else if (array.length == 3) {
					path.addCircle(array[0], array[1], array[2], Path.Direction.CW);
				} else if (array.length == 4) {
					Path.Direction dire = (array[3] == 0) ? Path.Direction.CW : Path.Direction.CCW;
					path.addCircle(array[0], array[1], array[2], dire);
				}
			} else if (a.key.equalsIgnoreCase("addOval") || a.key.equalsIgnoreCase("oval")) {
				float[] array = Utilities.getFloatArray(a.value, 0);
				if (array == null) {
					continue;
				} else if (array.length == 4) {
					RectF rectF = new RectF(array[0], array[1], array[2], array[3]);
					path.addOval(rectF, Path.Direction.CW);
				} else if (array.length == 5) {
					RectF rectF = new RectF(array[0], array[1], array[2], array[3]);
					Path.Direction dire = (array[4] == 0) ? Path.Direction.CW : Path.Direction.CCW;
					path.addOval(rectF, dire);
				}
			} else if (a.key.equalsIgnoreCase("addRect") || a.key.equalsIgnoreCase("rect")) {
				float[] array = Utilities.getFloatArray(a.value, 0);
				if (array == null) {
					continue;
				} else if (array.length == 4) {
					RectF rectF = new RectF(array[0], array[1], array[2], array[3]);
					path.addRect(rectF, Path.Direction.CW);
				} else if (array.length == 5) {
					RectF rectF = new RectF(array[0], array[1], array[2], array[3]);
					Path.Direction dire = (array[4] == 0) ? Path.Direction.CW : Path.Direction.CCW;
					path.addRect(rectF, dire);
				}
			} else if (a.key.equalsIgnoreCase("addRoundRect") || a.key.equalsIgnoreCase("RoundRect")) {
				float[] array = Utilities.getFloatArray(a.value, 0);
				if (array == null) {
					continue;
				} else if (array.length == 5) {
					RectF rectF = new RectF(array[0], array[1], array[2], array[3]);
					path.addRoundRect(rectF, array[4], array[4], Path.Direction.CW);
				} else if (array.length == 6) {
					RectF rectF = new RectF(array[0], array[1], array[2], array[3]);
					path.addRoundRect(rectF, array[4], array[5], Path.Direction.CW);
				} else if (array.length == 7) {
					RectF rectF = new RectF(array[0], array[1], array[2], array[3]);
					Path.Direction dire = (array[6] == 0) ? Path.Direction.CW : Path.Direction.CCW;
					path.addRoundRect(rectF, array[4], array[5], dire);
				} else if (array.length == 12) {
					RectF rectF = new RectF(array[0], array[1], array[2], array[3]);
					float[] corner = new float[8];
					for (int j = 0; j < 8; j++) {
						corner[j] = array[4 + j];
					}
					path.addRoundRect(rectF, corner, Path.Direction.CW);
				} else if (array.length == 13) {
					RectF rectF = new RectF(array[0], array[1], array[2], array[3]);
					float[] corner = new float[8];
					for (int j = 0; j < 8; j++) {
						corner[j] = array[4 + j];
					}
					Path.Direction dire = (array[12] == 0) ? Path.Direction.CW : Path.Direction.CCW;
					path.addRoundRect(rectF, corner, dire);
				}
			} else if (a.key.equalsIgnoreCase("polyline")) {
				Path newPath = getPolyLine(a.value, false);
				if (newPath != null) path.addPath(newPath);
			} else if (a.key.equalsIgnoreCase("polygon")) {
				Path newPath = getPolyLine(a.value, true);
				if (newPath != null) path.addPath(newPath);
			}
		}
		return path;
	}

	private static Path getPolyLine(String str, boolean close) {
		str = str.replace(" ", "");
		String[] s = str.split("\\),");
		if (s.length == 0) return null;
		Path path = new Path();
		for (int j = 0; j < s.length; j++) {
			String pair = s[j];
			while (pair.endsWith(")")) {
				pair = pair.substring(0, pair.length() - 1);
			}
			while (pair.startsWith("(")) {
				pair = pair.substring(1);
			}
			float[] pairFloats = Utilities.getFloatArray(pair, 2, 0);
			if (pairFloats != null) {
				if (path.isEmpty()) path.moveTo(pairFloats[0], pairFloats[1]);
				else path.lineTo(pairFloats[0], pairFloats[1]);
			}
		}
		if (close) path.close();
		return path;
	}

	public static Paint getBorder(String str) {
		if (str == null || str.trim().equals("")) return null;
		PaintSheet paintSheet = new PaintSheet();
		paintSheet.parse(str);
		return paintSheet.getPaint();
	}

	public static class Assign {
		public String key;
		public String value;
		public Assign(String str) {
			if (str == null || str.trim() == "") {
				this.key = null;
				this.value = null;
			} else {
				str = str.trim();
				String key, value;
				if (str.indexOf(":") <= 0) {
					key = str;
					value = null;
				} else {
					key = str.substring(0, str.indexOf(":"));
					value = str.substring(str.indexOf(":") + 1).trim();
				}
				key = key.trim().toLowerCase().replaceAll("\\-", "\\_");
				value = value.toLowerCase().replace("px", "");
				this.key = key;
				this.value = value;
			}
		}
		public boolean isNull() {
			return key == null || key.length() == 0;
		}
	}

}
